using System;
using System.Collections.Generic;
using Gj.Galaxy.Logic;
using Gj.Services;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
using BoxCollider = Unity.Physics.BoxCollider;
using CapsuleCollider = Unity.Physics.CapsuleCollider;
using SphereCollider = Unity.Physics.SphereCollider;

namespace Gj
{
    /// <summary>
    ///     对象基础control 网络层控制绑定
    /// </summary>
    public class BaseControl : MonoBehaviour, InitSyncListener, Control
    {
        protected const string TIME_OUT = "OnTime";
        public int birthTime;

        public int index;
        public ObjectInfo detail;
        public ObjectAttr attr;

        public GameObject skin;

        public float updateTime;

        private NetworkEsse _esse;

        private GameObject _gameObject;

        private Skin _skin;
        private Transform _transform;
        protected bool auto = false;
        protected ObjectControl control;
        public ObjectData data;
        protected bool debug = false;

        protected Entity entity;
        protected bool exist;
        protected Control master;

        public Action<NetworkEsse> RegisterAction = null;

        public Transform Transform
        {
            get
            {
                if (_transform == null) _transform = transform;
                return _transform;
            }
        }

        public GameObject GameObject
        {
            get
            {
                if (_gameObject == null) _gameObject = gameObject;
                return _gameObject;
            }
        }

        public NetworkEsse Esse
        {
            get => _esse != null && _esse.Id != 0 ? _esse : null;
            set
            {
                if (_esse == null) _esse = value;
            }
        }

        public Skin Skin
        {
            get
            {
                if (_skin == null) _skin = skin.GetComponent<Skin>();
                return _skin;
            }
        }

        private void OnDestroy()
        {
            Destroy();
        }

        public bool IsPlayer()
        {
            return control == ObjectControl.Player || control == ObjectControl.OtherPlayer;
        }

        /// <summary>
        ///     接收外部对象消息
        /// </summary>
        public virtual void OnMessage(Control obj, byte type, byte category, int value)
        {
        }

        public virtual void OnReady()
        {
        }

        public virtual void OnFinish()
        {
        }

        float UnityObject.GetRadius()
        {
            return detail.baseInfo.radius;
        }

        float UnityObject.GetHeight()
        {
            return detail.baseInfo.height;
        }

        public int GetIndex()
        {
            return index;
        }

        public object GetId()
        {
            return 0;
        }

        Entity CoreObject.GetEntity()
        {
            return entity;
        }

        public bool IsExist()
        {
            return exist;
        }

        public string GetName()
        {
            return detail.refInfo.name;
        }

        public string GetKey()
        {
            return detail.refInfo.name;
        }

        public bool IsAuto()
        {
            return auto;
        }

        public bool IsDebug()
        {
            return debug;
        }

        public Control GetMaster()
        {
            return master;
        }

        public int GetIdentity()
        {
            if (GetMaster() != null) return GetMaster().GetIdentity();
            return detail.identity;
        }

        GameObject UnityObject.GetGameObject()
        {
            return GameObject;
        }

        public bool IsEnemy(DataObject obj)
        {
            throw new NotImplementedException();
        }

        public bool IsPartner(DataObject obj)
        {
            throw new NotImplementedException();
        }

        public ObjectAttrInf GetAttr()
        {
            return attr;
        }

        public Skin GetSkin()
        {
            return Skin;
        }

        public DataObject GetData()
        {
            return null;
        }

        public int GetTotal()
        {
            return 1;
        }

        public int GetNumber()
        {
            return 1;
        }

        public Vector3 GetRealPosition()
        {
            return GetPosition();
        }

        public Quaternion GetRealRotation()
        {
            return GetRotation();
        }

        public Skin GetObject()
        {
            return Skin;
        }

        public Skin GetObject(int index)
        {
            return Skin;
        }

        public virtual Vector3 GetPosition()
        {
            return ObjectService.EntityManager.GetComponentData<LocalTransform>(entity).Position;
        }

        public virtual Quaternion GetRotation()
        {
            return ObjectService.EntityManager.GetComponentData<LocalTransform>(entity).Rotation;
        }

        public void SetPosition(Vector3 position)
        {
            ObjectService.EntityManager.AddComponentData(entity, new LocalTransform { Position = position });
        }

        public void SetRotation(Quaternion rotation)
        {
            ObjectService.EntityManager.AddComponentData(entity, new LocalTransform { Rotation = rotation });
        }


        public virtual void UpdatePosition(Vector3 position)
        {
            Transform.position = position;
        }

        public virtual void UpdateRotation(Quaternion rotation)
        {
            Skin?.SetRotation(rotation);
        }

        public NetworkEsse GetSync()
        {
            return _esse;
        }

        void InitSyncListener.InitSync(NetworkEsse esse)
        {
            _esse = esse;
            InitSync(esse);
        }

        void InitSyncListener.InitMeta(StreamWriter writer)
        {
            InitMeta(writer);
        }

        void InitSyncListener.OnInitMeta(StreamReader reader)
        {
            OnInitMeta(reader);
        }

        void InitSyncListener.InitData(StreamWriter writer)
        {
            InitData(writer);
        }

        void InitSyncListener.OnInitData(StreamReader reader)
        {
            OnInitData(reader);
        }

        void InitSyncListener.OnRegister(NetworkEsse esse)
        {
            if (RegisterAction != null) RegisterAction(esse);
            var entityData = ObjectService.EntityManager.GetComponentData<EntityData>(entity);
            entityData.id = esse.Id;
            ObjectService.EntityManager.SetComponentData(entity, entityData);
        }

        void InitSyncListener.OnAgent()
        {
            OnAgent();
        }

        void InitSyncListener.OnRelieved()
        {
            OnRelieved();
        }

        void InitSyncListener.OnDestroyed()
        {
            Destroy();
        }

        /// <summary>
        ///     设置改对象的entity
        /// </summary>
        /// <param name="skin"></param>
        protected void SetSkin(string skinName)
        {
            if (skin == null) skin = ObjectService.single.MakeSkin(skinName, gameObject);

            if (skin == null) Debug.LogErrorFormat("target control skin empty skinName: {0}", skinName);
        }


        /// <summary>
        ///     自动创建对象插件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="pluginName"></param>
        /// <returns></returns>
        protected T SetPlugin<T>(T t, string pluginName) where T : Component
        {
            if (t == null) t = ObjectService.single.MakePlugin<T>(pluginName, gameObject);
            return t;
        }

        /// <summary>
        ///     自动创建对象插件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="pluginName"></param>
        /// <returns></returns>
        protected T SetPlugin<T>(T t, string pluginName, GameObject obj) where T : Component
        {
            if (t == null) t = ObjectService.single.MakePlugin<T>(pluginName, obj);
            return t;
        }

        protected void InitBase(BaseInfo baseInfo)
        {
            InitBase(baseInfo, entity);
        }

        /// <summary>
        ///     根据对象基础属性创建对象模型信息
        /// </summary>
        /// <param name="baseAttr"></param>
        protected void InitBase(BaseInfo baseInfo, Entity entity)
        {
            if (baseInfo.collider != ObjectCollider.Empty)
            {
                var physicsCollider = new PhysicsCollider();
                switch (baseInfo.collider)
                {
                    case ObjectCollider.Box:
                        physicsCollider.Value = BoxCollider.Create(new BoxGeometry
                        {
                            Orientation = quaternion.identity,
                            Center = new float3(baseInfo.centerX, baseInfo.centerY, baseInfo.centerZ),
                            Size = new float3(baseInfo.sizeX, baseInfo.sizeY, baseInfo.sizeZ)
                        });
                        break;
                    case ObjectCollider.Sphere:
                        physicsCollider.Value = SphereCollider.Create(new SphereGeometry
                        {
                            Center = new float3(baseInfo.centerX, baseInfo.centerY, baseInfo.centerZ),
                            Radius = baseInfo.radius
                        });
                        break;
                    case ObjectCollider.Capsule:
                        physicsCollider.Value = CapsuleCollider.Create(new CapsuleGeometry
                        {
                            Vertex0 = new float3(0, 0, 0),
                            Vertex1 = new float3(0, baseInfo.height, 0),
                            Radius = baseInfo.radius
                        });
                        break;
                }

                ObjectService.EntityManager.AddComponentData(entity, physicsCollider);
            }

            if (baseInfo.rigidbody)
            {
                var r = UnityTools.GetComponentRequire<Rigidbody>(gameObject);
                r.isKinematic = baseInfo.kinematic;
                r.useGravity = baseInfo.gravity;
                r.drag = Mathf.Infinity;
                r.angularDrag = Mathf.Infinity;
                r.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ |
                                RigidbodyConstraints.FreezeRotationY;
            }
        }

        /// <summary>
        ///     初始化对象扩展属性
        /// </summary>
        /// <param name="objects"></param>
        protected virtual void FormatExtend(object[] objects)
        {
        }

        protected void InitInfo(ObjectInfo objectInfo, ObjectControl objectControl, GameObject obj)
        {
            detail = objectInfo;
            control = objectControl;
            attr = new ObjectAttr(objectInfo.attr, AttrService.single);
            data = new ObjectData(attr, AttrService.single);
            master = obj != null ? UnityTools.GetControl(obj) : null;
        }

        public bool IsMainPlayer()
        {
            return control == ObjectControl.Player;
        }

        public bool IsAiPlayer()
        {
            return control == ObjectControl.AiPlayer && Esse != null && Esse.IsAgent;
        }

        public virtual void Init()
        {
        }

        protected void InitEntity()
        {
            if (entity == Entity.Null) entity = ObjectService.EntityManager.CreateEntity();
            InitEntity(entity);
            ObjectService.EntityManager.AddComponentData(entity,
                new LocalTransform { Position = Transform.position, Rotation = Transform.rotation });
        }

        protected void InitEntity(Entity entity)
        {
            var identity = detail.identity;
            var category = detail.category;
            var masterEntity = Entity.Null;
            if (GetMaster() != null)
            {
                var master = GetMaster();
                identity = master.GetIdentity();
                masterEntity = master.GetEntity();
            }

            ObjectService.EntityManager.AddComponentData(entity,
                new EntityData
                {
                    index = index, identity = identity, category = category, master = masterEntity,
                    radius = ((Control)this).GetRadius()
                });
            transform.rotation = Quaternion.identity;
        }

        /// <summary>
        ///     创建对象初始化函数
        /// </summary>
        /// <param name="objectInfo"></param>
        /// <param name="control"></param>
        /// <param name="obj"></param>
        public void Init(ObjectInfo objectInfo, ObjectControl control, GameObject obj, int i)
        {
            index = i;
            exist = true;
            InitInfo(objectInfo, control, obj);
            InitEntity();
            InitBase(detail.baseInfo);
            SetSkin(objectInfo.skin);
            FormatExtend(detail.extend);
            Init();
        }

        protected virtual void OnRecovery()
        {
        }

        protected void Recovery()
        {
            OnRecovery();
            if (_esse != null)
                Esse.Destroy();
            else
                Destroy();
        }

        protected virtual void Destroy()
        {
            exist = false;
            ObjectService.EntityManager.DestroyEntity(entity);
            entity = Entity.Null;
            ControlService.single.DestroyControl(this);
        }

        public void UpdateData(string key, int value)
        {
            UpdateData(AttrService.single.DataIndex(key), value);
        }

        public void UpdateData(string[] keys, int[] values)
        {
            var indexs = new byte[keys.Length];
            for (var i = 0; i < keys.Length; i++) indexs[i] = AttrService.single.DataIndex(keys[i]);
            UpdateData(indexs, values);
        }

        public void UpdateData(byte index, int value)
        {
            data[index] += value;
            var v = UpdateDataSync(index, data[index]);
            OnUpdateData(index);

            if (AttrService.single.ContainsProcessData(index))
            {
                Debug.LogErrorFormat("updateData: {0} -> {1}", index, value);
                if (IsMainPlayer() || IsAiPlayer())
                    if (RoomConnect.inRoom)
                        RoomConnect.Process(new object[]
                            { new object[] { InfoKey.DataKey, new object[] { index, v, detail.refInfo.id } } });
            }
        }

        public void UpdateData(byte[] indexs, int[] values)
        {
            var d = new List<object>();
            for (var i = 0; i < indexs.Length; i++)
            {
                var index = indexs[i];
                data[index] += values[i];
                values[i] = UpdateDataSync(index, data[index]);

                if (AttrService.single.ContainsProcessData(index))
                {
                    Debug.LogErrorFormat("updateData:" + index);
                    object[] v;
                    if (IsMainPlayer() || IsAiPlayer())
                    {
                        v = new object[] { InfoKey.DataKey, new object[] { index, values[i], detail.refInfo.id } };
                        d.Add(v);
                    }
                }

                OnUpdateData(index);
            }

            if (d.Count > 0)
                if (RoomConnect.inRoom)
                    RoomConnect.Process(d.ToArray());
        }

        protected virtual void OnUpdateData(byte index)
        {
        }

        protected void UpdateExt(byte type, int category, float value)
        {
            var index = DataService.single.GetIndex(type, category);
            if (index == -1) return;
            var v = ChangeDataSync((ushort)index, value);
            OnUpdateData((byte)index);

            if (AttrService.single.ContainsProcessData((ushort)index))
            {
                Debug.LogErrorFormat("updateData:" + index);
                if (IsMainPlayer() || IsAiPlayer())
                    if (RoomConnect.inRoom)
                        RoomConnect.Process(new object[]
                            { new object[] { InfoKey.DataKey, new object[] { index, v, detail.refInfo.id } } });
            }
        }

        protected void UpdateFormat<T>(int index, T t)
        {
            UpdateDataSync((ushort)index, t);
        }

        /// <summary>
        ///     更新自身数据
        /// </summary>
        /// <param name="index"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private T UpdateDataSync<T>(ushort index, T value)
        {
            if (_esse != null)
            {
                var v = Esse.UpdateData(index, value, true);
                return v;
            }

            return value;
        }


        /// <summary>
        ///     更新自身数据
        /// </summary>
        /// <param name="index"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private float ChangeDataSync(ushort index, float value)
        {
            if (_esse != null)
            {
                var v = Esse.ChangeData(index, value);
                return v;
            }

            return value;
        }


        /// <summary>
        ///     获取自身数据
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        protected float GetData(ushort index)
        {
            if (_esse != null) return Esse.GetDataFloat(index);
            return 0;
        }


        /// <summary>
        ///     获取自身数据
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        protected T GetData<T>(ushort index)
        {
            if (_esse != null) return Esse.GetData<T>(index);
            return default;
        }

        /// <summary>
        ///     给指定对象控制器发送消息
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="type"></param>
        public void Message(Control obj, byte type)
        {
            Message(obj, type, 0);
        }

        public void Message(Control obj, byte type, byte category)
        {
            Message(obj, type, category, 0);
        }

        public void Message(Control obj, byte type, byte category, int value)
        {
            obj.OnMessage(this, type, category, value);
        }

        protected virtual void InitSync(NetworkEsse esse)
        {
        }

        /// <summary>
        ///     初始化数据
        /// </summary>
        /// <param name="stream"></param>
        protected virtual void InitData(StreamWriter writer)
        {
            for (var i = 0; i < data.length; i++) writer.SendNext(data[i]);
        }

        /// <summary>
        ///     接收数据
        /// </summary>
        /// <param name="stream"></param>
        protected virtual void OnInitData(StreamReader reader)
        {
            for (var i = 0; i < data.length; i++) data[i] = Convert.ToInt32(reader.ReceiveNext());
            OnInitData();
        }

        protected virtual void OnInitData()
        {
        }

        /// <summary>
        ///     初始化数据
        /// </summary>
        /// <param name="stream"></param>
        protected virtual void InitMeta(StreamWriter writer)
        {
            birthTime = DateTools.GetNowTime();
            if (detail.liveTime > 0)
            {
                writer.SendNext(birthTime);
                Invoke("OnTime", detail.liveTime);
            }
        }

        /// <summary>
        ///     接收数据
        /// </summary>
        /// <param name="stream"></param>
        protected virtual void OnInitMeta(StreamReader reader)
        {
            birthTime = reader.ReceiveNext<int>();
            if (detail.liveTime > 0)
            {
                // 全段是否超过生存时间
                var overTime = DateTools.GetNowTime() - birthTime + detail.liveTime;
                if (overTime > 0)
                    Invoke("TimeOut", overTime);
                else
                    TimeOut();
            }
        }

        public void TimeOut()
        {
            Esse.Message(TIME_OUT);
        }

        /// <summary>
        ///     时间到异步回调
        /// </summary>
        [Message(MessageExcuter.Agent, TIME_OUT)]
        protected void TimeOutBack()
        {
            OnTime();
        }

        protected virtual void OnTime()
        {
        }

        protected virtual void OnAgent()
        {
        }

        protected virtual void OnRelieved()
        {
        }

        public void Trigger(AttrEventInfo info, byte index, AttrEnv attrEnv)
        {
            if (detail.attrEvent == null) return;
            var attrEvent = detail.attrEvent.Get(index);
            if (attrEvent == null) return;
            AttrService.tools.AdditionHandle(info, attrEvent, attrEnv);
        }

        public void Handle(AttrEventInfo info, AttrEvent attrEvent, AttrEnv attrEnv)
        {
            if (attrEvent == null) return;
            AttrService.tools.AdditionHandle(info, attrEvent, attrEnv);
        }

        public virtual string GetObjectType()
        {
            return "base";
        }

        public virtual float GetMoveSpeed()
        {
            return detail.baseInfo.speed;
        }

        public virtual float GetRotateSpeed()
        {
            return detail.baseInfo.rotate;
        }

        public int GetCategory()
        {
            return detail.category;
        }

        public void SetUpdateTime(float time)
        {
            updateTime = time;
        }

        public float GetUpdateTime()
        {
            return updateTime;
        }

        protected void Log(string t)
        {
            LogTools.Info("{0} id->{1} index->{2} {3}", GetName(), GetId(), GetIndex(), t);
        }

        protected void Log(string t, string s, params object[] p)
        {
            var text = string.Format(s, p);
            LogTools.Info("{0} id->{1} index->{2} {3} {4}", GetName(), GetId(), GetIndex(), t, text);
        }
    }
}